package ru.chaykin.quadtree;

import static ru.chaykin.quadtree.QuadElement.CHILDREN_NUM;
import ru.chaykin.primitives.Circle;

/**
 * Класс для построение дерева
 * @author k.chaykin
 *
 */
public class QuadTreeBuilder {
	
	public static final int MAX_OBJ_COUNT = 1024;
	private static volatile QuadElement root;
	
	public QuadTreeBuilder(Quad bbox) {
		root = new QuadElement(bbox);
		
	}

	public void addObject(float[] obj) {
		addObject(root, obj); //Метод рекурсивно пройдет во всему дереву и положит объект куда надо.
	}
	
	private void addObject(QuadElement quadEl, float[] obj) {
		if (quadEl.getCount() < MAX_OBJ_COUNT) { //Делить ничего не надо, просто добалвяем новый элемент
			quadEl.addObj(obj);
		}
		else {
			QuadElement[] quadEls = quadEl.getChildren(); 
			if (!quadEl.hasChildren()) { //Нужно создать новый уровень вложенности
				Quad bbox = quadEl.getBoundingBox();
				float cx = (bbox.getLeft() + bbox.getRight()) / 2;
				float cy = (bbox.getTop() + bbox.getBottom()) / 2;
				
				quadEls = new QuadElement[CHILDREN_NUM];
				quadEls[0] = new QuadElement(new Quad(bbox.getLeft(), cx, cy, bbox.getBottom()));
				quadEls[1] = new QuadElement(new Quad(cx, bbox.getRight(), cy, bbox.getBottom()));
				quadEls[2] = new QuadElement(new Quad(bbox.getLeft(), cx, bbox.getTop(), cy));
				quadEls[3] = new QuadElement(new Quad(cx, bbox.getRight(), bbox.getTop(), cy));
				for (float[] existObj : quadEl.getObjs()) {
					Circle circle = new Circle(existObj[0], existObj[1], existObj[2]);
					for (QuadElement el : quadEls) {
						if (el.getBoundingBox().in(circle)) {
							el.addObj(existObj); //Переносим все объекты из текущего подпространства во вложенные
						}
					}
				}
				quadEl.clearObjs();
				quadEl.setChildren(quadEls); //Установим потомков
				addObjectToChildren(obj, quadEls); //Добавляем текущий объект
			}
			else { //Более низкий уровень уже есть, вызовем функцию рекурсивно
				addObjectToChildren(obj, quadEls); //Добавляем текущий объект
			}
		}
	}

	private void addObjectToChildren(float[] obj, QuadElement[] quadEls) {
		Circle circle = new Circle(obj[0], obj[1], obj[2]);
		for (QuadElement el : quadEls) {
			if (el.getBoundingBox().in(circle))
				addObject(el, obj);
		}
	}
	
	public static QuadElement getRootQuad() {
		return root;
	}
}
